package Skal;

use Mojo::Base 'Mojolicious';
use DBI;

sub startup {
    my $self = shift;
    # Application object

    $self->secrets([
        'Mojolicious_Skal_Secret_Passphrase_for_sessions_D54fdgseds',
    ]);
    # Mojolicious application secret passphrases
    # http://mojolicio.us/perldoc/Mojolicious#secrets

    $self->sessions->cookie_name('Mojolicious_Skal_Cookie');
    # Mojolicious sessions cookie name
    # http://mojolicio.us/perldoc/Mojolicious/Sessions#cookie_name

    $self->config({
        hypnotoad => {
            listen => ['http://*:80/']
        }
    });
    # Hypnotoad server configuration
    # Listen of port 80

    $self->hook(before_dispatch => sub {
        my $self = shift;
        # Controller object

        my $url = $self->req->url->to_abs;

        if ($url->host eq 'localhost' or
            $url->host eq '127.0.0.1' or
            $url->host eq 'www.mezl.eu'
        ) {
            return
                unless ($url->path->trailing_slash);
            return $self->redirect_to($url->path->trailing_slash(0)->to_string);
        }
        $url->host('www.mezl.eu');
        return $self->redirect_to($url->to_string);
    });
    # Redirect to http://www.mezl.eu/ for URL http://mezl.eu/
    # Redirect to path without trailing slash for path with trailing slash

    my $s_u_n;
    # Skal user name

    $self->helper(get_user_name_helper => sub {
        my $self = shift;
        # Controller object

        return $s_u_n;
    });

    $self->helper(root_url_helper => sub {
        my $self = shift;
        # Controller object

        return $ENV{SKAL_MOUNT} ? '/skal' : q{};
    });

    $self->helper(mojo_date_string_helper => sub {
        my $self = shift;
        # Controller object
        my $for  = shift;
        # 'human' for human readable datetime string or
        # 'machine' for datetime string which can be easily parsed

        return Mojo::Date->new->to_string
            if ($for eq 'human');
        return Mojo::Date->new->to_datetime
            if ($for eq 'machine');
        return Mojo::Date->new->epoch;
    });

    my $r = $self->routes;

    my $auth_section = $r->under(sub {
        my $self = shift;
        # Controller object

        $self->res->headers->www_authenticate('Basic');

        my $auth_subref = sub {
            my $text_message = shift;

            $self->render(
                text   => $text_message,
                status => 401
            );
            return undef;
        };

        if (my $userinfo = $self->req->url->to_abs->userinfo) {
            if ($userinfo =~ /([^:]+):(.+)/xms) {
                my $skal_user_name = $1;
                my $skal_user_pass = $2;

                if ($skal_user_name =~ /[\W\s]/xms || $skal_user_name =~ /(?:_{2,})+/xms) {
                    return &{$auth_subref}('Wrong username or password!');
                }
                # User name can contain only alphanumeric characters plus _ character
                # To defence against SQL injection

                my $dbh = DBI->connect(
                    "DBI:SQLite:dbname=$ENV{SKAL_ROOT}/db/skaldb.sqlite3",
                    q{},
                    q{},
                    {RaiseError => 1}
                ) or die $DBI::errstr;

                my $stmt = qq(select password from users where name = '$skal_user_name';);
                my $sth  = $dbh->prepare($stmt);
                my $rv   = $sth->execute or die $DBI::errstr;
                die $DBI::errstr if ($rv < 0);
                my @row_vals;
                while (my @row = $sth->fetchrow_array) {
                    push @row_vals, @row;
                }
                $dbh->disconnect;
                if (scalar @row_vals == 1) {
                    my $usrpasswd = $row_vals[0];
                    if ($usrpasswd && crypt($skal_user_pass, $usrpasswd) eq $usrpasswd) {
                        $s_u_n = $skal_user_name;
                        # Don't forget to set Skal user name for helper
                        return 1;
                    }
                    return &{$auth_subref}('Wrong username or password!');
                }
                return &{$auth_subref}('Wrong username or password!')
                    if (scalar @row_vals == 0);
                return &{$auth_subref}('Error while logging in!');
            }
            return &{$auth_subref}('Wrong username or password!');
        }
        return &{$auth_subref}('Authentication required!');
    });

    $auth_section->get('/')
                 ->to(
                     controller => 'index',
                     action     => 'index'
                 );
    $auth_section->get('/result')
                 ->to(
                     controller => 'index',
                     action     => 'result'
                 );
    $auth_section->get('/archive')
                 ->to(
                     controller => 'index',
                     action     => 'archive'
                 );
    $auth_section->get('/help')
                 ->to(
                     controller => 'index',
                     action     => 'help'
                 );
    $auth_section->get('/conf')
                 ->to(
                     controller => 'index',
                     action     => 'configuration'
                 );

    $auth_section->get('/ahoj')
                 ->to(
                     controller => 'index',
                     action     => 'ahoj_1'
                 );
    # Route will be removed
    $auth_section->get('/ahoj/znova')
                 ->to(
                     controller => 'index',
                     action     => 'ahoj_2'
                 );
    # Route will be removed

    return;
}

1;

__END__

=pod

This Mojolicious application can be mounted and embedded into some other
Mojolicious application with use of Mount plugin.

Don't forget to check whether $ENV{SKAL_MOUNT} is true (1, application is
mounted and embedded) or false (0, application is not mounted and embedded)
in controller scripts and templates. There is a helper root_url_helper for
that purpose.

If it's true then use prefix '/skal' for URLs in all links.

If it's false then use URLs without prefix in all links.

=cut

